#include "wm_pmu.h"

/**
  * @brief  PMU initialization
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_Init(PMU_HandleTypeDef *hpmu)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_PMU_ALL_INSTANCE(hpmu->Instance));
	assert_param(IS_PMU_CLKSOURCE(hpmu->ClkSource));
	
	HAL_PMU_MspInit(hpmu);
	SET_BIT(hpmu->Instance->CR, PMU_CR_32KRC_CAL_EN);
	if (hpmu->ClkSource == PMU_CLKSOURCE_32RC)
	{
		CLEAR_BIT(hpmu->Instance->CR, PMU_CR_32KRCBYPASS);
	}
	else
	{
		SET_BIT(hpmu->Instance->CR, PMU_CR_32KRCBYPASS);
		SET_BIT(hpmu->Instance->XTAL, PMU_XTAL_40M_EN);
	}
	
	return HAL_OK;
}

/**
  * @brief  PMU deinitialization
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_DeInit(PMU_HandleTypeDef *hpmu)
{
	HAL_PMU_MspDeInit(hpmu);
	SET_BIT(hpmu->Instance->IF, (PMU_IF_SLEEP | PMU_IF_STANDBY | PMU_IF_TIM0 | PMU_IF_IO_WAKE | PMU_IF_RTC));
	CLEAR_BIT(hpmu->Instance->TIMER0, PMU_TIMER0_EN);
	CLEAR_BIT(hpmu->Instance->RTCCR0, PMU_RTCCR0_TIMING_EN);
	CLEAR_BIT(hpmu->Instance->RTCCR1, PMU_RTCCR1_EN);
	CLEAR_BIT(hpmu->Instance->CR, (PMU_CR_SLEEP_EN | PMU_CR_STANDBY_EN));
	
	return HAL_OK;
}

/**
  * @brief  PMU msp initialization
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
__attribute__((weak)) void HAL_PMU_MspInit(PMU_HandleTypeDef *hpmu)
{
	UNUSED(hpmu);
}

/**
  * @brief  PMU msp deinitialization
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
__attribute__((weak)) void HAL_PMU_MspDeInit(PMU_HandleTypeDef *hpmu)
{
	UNUSED(hpmu);
}

/**
  * @brief  PMU sleep mode enable
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
void HAL_PMU_Enter_Sleep(PMU_HandleTypeDef *hpmu)
{
	SET_BIT(hpmu->Instance->CR, PMU_CR_SLEEP_EN);
}

/**
  * @brief  PMU standby mode enable
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
void HAL_PMU_Enter_Standby(PMU_HandleTypeDef *hpmu)
{
	SET_BIT(hpmu->Instance->CR, PMU_CR_STANDBY_EN);
}

/**
  * @brief  PMU TIMER0 set and start
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @param  Period a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. The unit is seconds.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_TIMER0_Start(PMU_HandleTypeDef *hpmu, uint32_t Period)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_PMU_ALL_INSTANCE(hpmu->Instance));
	assert_param(IS_PMU_TIMPERIOD(Period));
	
	WRITE_REG(hpmu->Instance->TIMER0, (PMU_TIMER0_EN | Period));
	
	return HAL_OK;
}

/**
  * @brief  PMU TIMER0 stop
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_TIMER0_Stop(PMU_HandleTypeDef *hpmu)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_PMU_ALL_INSTANCE(hpmu->Instance));
	
	CLEAR_BIT(hpmu->Instance->TIMER0, PMU_TIMER0_EN);
	
	return HAL_OK;
}

/**
  * @brief  PMU TIMER1 set and start
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @param  Period a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. The unit is millisecond.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_TIMER1_Start(PMU_HandleTypeDef *hpmu, uint32_t Period)
{
	uint32_t val = 0x10000;
	
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_PMU_ALL_INSTANCE(hpmu->Instance));
	assert_param(IS_PMU_TIMPERIOD(Period));
	
	if (hpmu->ClkSource == PMU_CLKSOURCE_32RC)
	{
		if (Period < 5)
		{
			Period = 5;
		}
		val |= (Period - 5);
	}
	else
	{
		val |= (Period - 1);
	}
	
	WRITE_REG(hpmu->Instance->TIMER1, val);
	
	return HAL_OK;
}

/**
  * @brief  PMU TIMER1 stop
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_TIMER1_Stop(PMU_HandleTypeDef *hpmu)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_PMU_ALL_INSTANCE(hpmu->Instance));
	
	CLEAR_BIT(hpmu->Instance->TIMER1, ((1 << 16) | (1 << 17)));
	
	return HAL_OK;
}

/**
  * @brief  RTC get time
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @param  Time pointer to a RTC_TimeTypeDef structure that used to save the result
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_RTC_GetTime(PMU_HandleTypeDef *hpmu, RTC_TimeTypeDef *Time)
{
	uint32_t cr0, cr1;
	
	if ((hpmu == NULL) | (Time == NULL))
	{
		return HAL_ERROR;
	}
	assert_param(IS_RTC_ALL_INSTANCE(hpmu->Instance));
	
	cr0 = READ_REG(hpmu->Instance->RTCCR0);
	cr1 = READ_REG(hpmu->Instance->RTCCR1);
	
	Time->Year = ((cr1 & PMU_RTCCR1_YEAR) >> PMU_RTCCR1_YEAR_Pos);
	Time->Month = ((cr1 & PMU_RTCCR1_MONTH) >> PMU_RTCCR1_MONTH_Pos);
	
	Time->Date = ((cr0 & PMU_RTCCR0_DATE) >> PMU_RTCCR0_DATE_Pos);
	Time->Hours = ((cr0 & PMU_RTCCR0_HOUR) >> PMU_RTCCR0_HOUR_Pos);
	Time->Minutes = ((cr0 & PMU_RTCCR0_MINUTE) >> PMU_RTCCR0_MINUTE_Pos);
	Time->Seconds = ((cr0 & PMU_RTCCR0_SECOND) >> PMU_RTCCR0_SECOND_Pos);
	
	return HAL_OK;
}

/**
  * @brief  RTC set time and start
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @param  Time pointer to a RTC_TimeTypeDef structure that contains the initial value of time
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_RTC_Start(PMU_HandleTypeDef *hpmu, RTC_TimeTypeDef *Time)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_RTC_ALL_INSTANCE(hpmu->Instance));
	
	CLEAR_BIT(hpmu->Instance->RTCCR1, PMU_RTCCR1_EN);
	MODIFY_REG(hpmu->Instance->RTCCR0, (PMU_RTCCR0_DATE | PMU_RTCCR0_HOUR | PMU_RTCCR0_MINUTE | PMU_RTCCR0_SECOND),
					((Time->Date << PMU_RTCCR0_DATE_Pos) | (Time->Hours << PMU_RTCCR0_HOUR_Pos) | 
					(Time->Minutes << PMU_RTCCR0_MINUTE_Pos) | (Time->Seconds << PMU_RTCCR0_SECOND_Pos)));
					
	MODIFY_REG(hpmu->Instance->RTCCR1, (PMU_RTCCR1_YEAR | PMU_RTCCR1_MONTH),
					((Time->Year << PMU_RTCCR1_YEAR_Pos) | (Time->Month << PMU_RTCCR1_MONTH_Pos)));
					
	SET_BIT(hpmu->Instance->RTCCR1, PMU_RTCCR1_EN);
	
	return HAL_OK;
}

/**
  * @brief  RTC stop
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_RTC_Stop(PMU_HandleTypeDef *hpmu)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_RTC_ALL_INSTANCE(hpmu->Instance));
	
	CLEAR_BIT(hpmu->Instance->RTCCR1, PMU_RTCCR1_EN);
	
	return HAL_OK;
}

/**
  * @brief  RTC set alram time and enable
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @param  Time pointer to a RTC_TimeTypeDef structure that contains the alarm value
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_RTC_Alarm_Enable(PMU_HandleTypeDef *hpmu, RTC_TimeTypeDef *Time)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_RTC_ALL_INSTANCE(hpmu->Instance));
	
	SET_BIT(hpmu->Instance->IF, PMU_IF_RTC);
	
	MODIFY_REG(hpmu->Instance->RTCCR1, (PMU_RTCCR1_YEAR | PMU_RTCCR1_MONTH),
					((Time->Year << PMU_RTCCR1_YEAR_Pos) | (Time->Month << PMU_RTCCR1_MONTH_Pos)));
					
	MODIFY_REG(hpmu->Instance->RTCCR0, (PMU_RTCCR0_DATE | PMU_RTCCR0_HOUR | PMU_RTCCR0_MINUTE | PMU_RTCCR0_SECOND),
					((Time->Date << PMU_RTCCR0_DATE_Pos) | (Time->Hours << PMU_RTCCR0_HOUR_Pos) | 
					(Time->Minutes << PMU_RTCCR0_MINUTE_Pos) | (Time->Seconds << PMU_RTCCR0_SECOND_Pos) | PMU_RTCCR0_TIMING_EN));
						
	return HAL_OK;
}

/**
  * @brief  RTC alarm disable
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_PMU_RTC_Alarm_Disable(PMU_HandleTypeDef *hpmu)
{
	if (hpmu == NULL)
	{
		return HAL_ERROR;
	}
	assert_param(IS_RTC_ALL_INSTANCE(hpmu->Instance));
	
	CLEAR_BIT(hpmu->Instance->RTCCR0, PMU_RTCCR0_TIMING_EN);
	
	return HAL_OK;
}

/**
  * @brief  PMU timer0 callback
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
__attribute__((weak)) void HAL_PMU_Tim0_Callback(PMU_HandleTypeDef *hpmu)
{
	UNUSED(NULL);
}

/**
  * @brief  PMU timer1 callback
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
__attribute__((weak)) void HAL_PMU_Tim1_Callback(PMU_HandleTypeDef *hpmu)
{
	UNUSED(NULL);
}

/**
  * @brief  PMU io wakeup callback
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
__attribute__((weak)) void HAL_PMU_IO_Callback(PMU_HandleTypeDef *hpmu)
{
	UNUSED(NULL);
}

/**
  * @brief  PMU rtc callback
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
__attribute__((weak)) void HAL_PMU_RTC_Callback(PMU_HandleTypeDef *hpmu)
{
	UNUSED(NULL);
}

/**
  * @brief  PMU irq handler
  * @param  hpmu pointer to a PMU_HandleTypeDef structure that contains
  *         the configuration information for pmu module
  * @retval HAL status
  */
void HAL_PMU_IRQHandler(PMU_HandleTypeDef *hpmu)
{
	uint32_t flag = READ_REG(hpmu->Instance->IF);
	
	SET_BIT(hpmu->Instance->IF, (PMU_IF_SLEEP | PMU_IF_STANDBY | PMU_IF_TIM0 | PMU_IF_TIM1 | PMU_IF_IO_WAKE | PMU_IF_RTC));
	if ((flag & PMU_IF_TIM0) == PMU_IF_TIM0)
	{
		HAL_PMU_Tim0_Callback(hpmu);
	}
	if ((flag & PMU_IF_TIM1) == PMU_IF_TIM1)
	{
		HAL_PMU_Tim1_Callback(hpmu);
	}
	if ((flag & PMU_IF_IO_WAKE) == PMU_IF_IO_WAKE)
	{
		HAL_PMU_IO_Callback(hpmu);
	}
	if ((flag & PMU_IF_RTC) == PMU_IF_RTC)
	{
		HAL_PMU_RTC_Callback(hpmu);
	}
}
